package luke.media.video;

import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;

import java.awt.*;
import java.awt.image.BufferedImage;

import java.util.List;
import java.util.ArrayList;
import java.util.Random;

import luke.system.callback.ProgressCallback;
// 添加必要的导入


/**
 * 频谱视频生成器类，用于生成带有播放列表和实时频谱能量柱状动画的视频。
 * 视频渲染器只负责根据频带能量画图、画播放列表和抬头时间，生成没有音频的视频
 */
public class SpectrumStarlightVideoGenerator extends BaseSpectrumVideoGenerator {
    // 星星粒子系统
    private List<VideoUIUtils.StarParticle> starParticles = new ArrayList<>();
    private Random random = new Random();
    private int globalFrameOffset = 0; // 全局帧索引偏移量

    /**
     * 构造函数 - 简化版本，只保留必要参数
     * 
     * @param outputVideoPath 输出视频路径
     * @param width 视频宽度
     * @param height 视频高度
     * @param playlistContent 播放列表内容
     * @param videoTitle 视频标题
     * @param fps 视频帧率（由调度器传入）
     * @param requiredBarCount 所需频段数量（由调度器传入）
     */
    public SpectrumStarlightVideoGenerator(String outputVideoPath, 
                                          int width, 
                                          int height, 
                                          List<String> playlistContent, 
                                          String videoTitle,
                                          double fps,
                                          int requiredBarCount) {
        super(outputVideoPath, width, height, playlistContent, videoTitle, fps, requiredBarCount);
    }

    /**
     * 设置全局帧索引偏移量（用于分段视频处理）
     * @param offset 偏移量
     */
    public void setGlobalFrameOffset(int offset) {
        this.globalFrameOffset = offset;
    }

    @Override
    public int getRequiredBarCount() {
        return super.getRequiredBarCount();
    }
    
    /**
     * 生成带有播放列表和实时频谱能量柱状动画的视频
     * @param durationMs 视频总时长（毫秒）
     * @param progressCallback 进度回调
     * @return 生成成功返回true，失败返回false
     */
    public boolean generateVideoWithSpectrum(double durationMs, ProgressCallback progressCallback) {
        FFmpegFrameRecorder recorder = null;

        try {
            // 设置视频参数（帧率已由调度器传入）
            double durationInSeconds = durationMs / 1000.0;
            this.totalFrames = (int) (durationInSeconds * fps);
            
            // 检查总帧数是否有效
            if (totalFrames <= 0) {
                System.err.println("无效的视频时长或帧率，总帧数: " + totalFrames);
                return false;
            }

            System.out.println("总时长: " + durationInSeconds + "秒, 总帧数: " + totalFrames);

            // 创建视频录制器
            recorder = new FFmpegFrameRecorder(outputVideoPath, width, height);
            // 使用无损格式提高合并速度
            if (outputVideoPath.endsWith("_segment_0.mp4") || outputVideoPath.contains("_segment_")) {
                // 对于中间片段，使用无损格式
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_FFV1); // 使用FFV1无损编解码器
                recorder.setFormat("matroska"); // 使用MKV容器格式
            } else {
                // 对于最终输出，使用标准MP4格式
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.setFormat("mp4");
            }
            recorder.setFrameRate(fps); // 使用调度器传入的帧率
            recorder.setPixelFormat(0);
            recorder.setVideoBitrate(2000000); // 提高比特率以获得更好的视频质量

            // 启动录制器
            recorder.start();

            // 生成视频帧
            for (long frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
                Java2DFrameConverter converter = null;
                BufferedImage frameImage = null;
                org.bytedeco.javacv.Frame frame = null;

                try {
                    // 计算当前时间点
                    double currentTime = frameIndex / fps;

                    // 创建视频帧
                    frameImage = createVideoFrame((int) frameIndex);

                    // 创建帧转换器
                    converter = new Java2DFrameConverter();

                    // 转换为Frame并记录
                    frame = converter.convert(frameImage);
                    recorder.record(frame);
                } finally {
                    // 确保所有资源都被正确释放
                    try {
                        if (frame != null) {
                            frame.close();
                        }
                        if (frameImage != null) {
                            frameImage.flush();
                        }
                        if (converter != null) {
                            converter.close();
                        }
                    } catch (Exception e) {
                        System.err.println("释放帧资源时出错: " + e.getMessage());
                    }
                }

                // 更新进度
                if (frameIndex % 30 == 0) { // 每30帧更新一次进度
                    double progress = (double) frameIndex / totalFrames * 100;
                    System.out.printf("进度: %.1f%%, 已处理帧数: %d/%d%n", progress, frameIndex, totalFrames);

                    if (progressCallback != null) {
                        progressCallback.onProgress("正在生成视频帧 " + (frameIndex+1) + "/" + totalFrames, progress);
                    }

                    // 定期进行垃圾回收，减少内存占用
                    if (frameIndex % 150 == 0) {
                        System.gc();
                    }
                }
            }

            System.out.println("视频生成成功: " + outputVideoPath);
            if (progressCallback != null) {
                progressCallback.onProgress("视频生成完成", 100);
            }
            return true;

        } catch (Exception e) {
            System.err.println("视频生成失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        } finally {
            // 确保所有资源都被正确释放
            try {
                if (recorder != null) {
                    recorder.stop();
                    recorder.release();
                }
            } catch (Exception e) {
                System.err.println("释放录制器资源时出错: " + e.getMessage());
            }

            // 强制进行垃圾回收
            System.gc();
        }
    }
    
    @Override
    public boolean generateVideo(long durationMs, ProgressCallback progressCallback) {
        return generateVideoWithSpectrum(durationMs, progressCallback);
    }

    /**
     * 创建视频帧，包含播放列表和频谱动画
     * @param frameIndex 帧索引
     * @return 视频帧图像
     */
    private BufferedImage createVideoFrame(int frameIndex) {
        // 创建图像，使用TYPE_3BYTE_BGR以避免颜色异常
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
        Graphics2D g2d = null;

        try {
            g2d = image.createGraphics();

            // 启用抗锯齿
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            // 绘制星空背景
            drawStarfieldBackground(g2d, frameIndex);

            // 绘制频谱动画
            drawSpectrumAnimation(g2d, frameIndex);

            // 绘制播放列表
            drawPlaylist(g2d);

            // 更新和绘制星星粒子（使用全局帧索引）
            VideoUIUtils.updateAndDrawStarParticles(g2d, starParticles, width, height, frameIndex, frameIndex + globalFrameOffset, random);
        } finally {
            // 确保Graphics2D资源被释放
            if (g2d != null) {
                g2d.dispose();
            }
        }

        return image;
    }

    /**
     * 绘制星空背景
     * @param g2d Graphics2D对象
     * @param frameIndex 帧索引
     */
    private void drawStarfieldBackground(Graphics2D g2d, int frameIndex) {
        // 使用统一的星空背景绘制方法
        VideoUIUtils.drawStarfieldBackground(g2d, width, height, frameIndex);
    }

    /**
     * 绘制频谱动画
     * @param g2d Graphics2D对象
     * @param frameIndex 帧索引
     */
    private void drawSpectrumAnimation(Graphics2D g2d, int frameIndex) {
        // 计算绘制区域（放在底部，增大尺寸）
        int barCount = 64; // 柱状图数量
        int spacing = 3; // 柱状图间距
        // 精确计算每个柱状图的宽度，确保总宽度能居中
        int totalSpacing = (barCount - 1) * spacing;
        int availableWidth = width - 80; // 减去左右边距
        int barWidth = (availableWidth - totalSpacing) / barCount;
        int spectrumWidth = barCount * barWidth + totalSpacing; // 实际频谱宽度
        int spectrumHeight = height / 2; // 高度为屏幕的一半
        int spectrumX = (width - spectrumWidth) / 2; // 居中显示
        int spectrumY = height - spectrumHeight - 30; // 放在底部，距离底部30像素

        // 绘制频谱柱状图（静止的实时柱状图）
        drawSpectrumBars(g2d, spectrumX, spectrumY, spectrumWidth, spectrumHeight, frameIndex, barCount, barWidth, spacing);
    }

    /**
     * 绘制频谱柱状图
     * @param g2d Graphics2D对象
     * @param x X坐标
     * @param y Y坐标
     * @param width 宽度
     * @param height 高度
     * @param frameIndex 帧索引
     * @param barCount 柱状图数量
     * @param barWidth 每个柱状图的宽度
     * @param spacing 柱状图间距
     */
    private void drawSpectrumBars(Graphics2D g2d, int x, int y, int width, int height, int frameIndex, int barCount, int barWidth, int spacing) {
        // 生成当前时间点的频谱数据（显示当前时刻各频率的能量切片）
        float[] spectrumData = generateCurrentSpectrumData(frameIndex, barCount);

        // 设置最大柱状图高度（相对固定，不随分辨率变化）
        int maxBarHeight = height - 10; // 使用大部分可用高度，使柱状图更高

        // 绘制每个柱状图
        for (int i = 0; i < barCount; i++) {
            // 计算柱状图高度（基于频谱数据）
            int barHeight = (int) (spectrumData[i] * maxBarHeight);

            // 确保柱状图高度不为负数
            barHeight = Math.max(0, barHeight);

            // 计算柱状图位置（固定位置，不会移动）
            int barX = x + i * (barWidth + spacing);
            // 柱状图底部对齐到绘制区域底部
            int barY = y + height - barHeight;

            // 根据频率设置颜色（优化的蓝紫色渐变）
            float frequencyRatio = (float) i / barCount;
            Color barColor = getBluePurpleColor(frequencyRatio);

            // 绘制柱状图主体
            g2d.setColor(barColor);
            g2d.fillRoundRect(barX, barY, barWidth, barHeight, 6, 6); // 增大圆角

            // 添加柱状图边框
            g2d.setColor(getBorderColor(barColor));
            g2d.setStroke(new BasicStroke(2)); // 更粗的边框
            g2d.drawRoundRect(barX, barY, barWidth, barHeight, 6, 6);

            // 添加柱状图顶部的高光效果
            if (barHeight > 10) { // 增加高光效果的最小高度要求
                g2d.setColor(getHighlightColor(barColor));
                g2d.fillRoundRect(barX, barY, barWidth, Math.min(15, barHeight), 6, 6); // 增大高光区域
            }
        }
    }

    /**
     * 绘制播放列表
     */
    protected void drawPlaylist(Graphics2D g2d) {
        VideoUIUtils.drawPlaylist(g2d, playlistContent, width, height, videoTitle);
    }

    /**
     * 生成当前帧的频谱数据
     * @param frameIndex 帧索引
     * @param barCount 柱状图数量
     * @return 频谱数据数组
     */
    private float[] generateCurrentSpectrumData(int frameIndex, int barCount) {
        // 如果有预计算的频谱数据，使用预计算的数据
        if (precomputedSpectrums != null && precomputedSpectrums.length > 0) {
            // 计算当前帧在预计算频谱数据中的索引
            int spectrumIndex = Math.min(frameIndex, precomputedSpectrums.length - 1);
            
            // 将频谱数据转换为柱状图数据
            float[] spectrumData = new float[barCount];
            double[] currentSpectrum = precomputedSpectrums[spectrumIndex];
            int spectrumLength = currentSpectrum.length;

            // 计算频谱数据的最大值，用于归一化
            double maxSpectrumValue = 0;
            for (int i = 0; i < spectrumLength; i++) {
                if (currentSpectrum[i] > maxSpectrumValue) {
                    maxSpectrumValue = currentSpectrum[i];
                }
            }

            // 避免除零错误
            if (maxSpectrumValue == 0) {
                maxSpectrumValue = 1;
            }

            // 将频谱数据映射到柱状图并增强视觉效果
            for (int i = 0; i < barCount; i++) {
                // 计算在频谱数据中的索引
                int index = (int) ((double) i / barCount * spectrumLength);
                index = Math.max(0, Math.min(index, spectrumLength - 1));

                // 获取频谱值并归一化
                double value = currentSpectrum[index];

                // 使用更柔和的归一化方式，提高小值可见性
                double normalizedValue = value / maxSpectrumValue;

                // 应用更强的非线性增强函数（五次方根）以提高小值的可见性
                value = Math.pow(normalizedValue, 1.0/3.0);

                // 进一步放大低频部分以增强视觉效果
                if (i < barCount / 4) {
                    value *= 1.3; // 更大幅度增强低频部分
                } else if (i < barCount * 3 / 4) {
                    value *= 1.6; // 更大幅度增强中频部分
                } else {
                    value *= 1.6; // 更大幅度增强高频部分
                }

                // 应用对数增强使柱状图更加猛烈，增强小值可见性
                if (value > 0) {
                    value = Math.log10(1 + 5 * value); // 增强对数参数以提高小值可见性
                }

                // 适度放大整体值以使柱状图更高，但避免过度放大导致截断
                value *= 1.4; // 增加整体放大倍数

                // 确保有一个最小值，使小能量也能显示
                value = Math.max(value, 0.0012);

                // 确保值在0-1范围内
                spectrumData[i] = (float) Math.min(1.0, Math.max(0, value));
            }

            return spectrumData;
        }
        // 视频渲染器不应该实时计算频谱数据
        // 如果没有预计算的频谱数据，返回默认数据
        else {
            // 如果没有预计算的频谱数据，返回默认数据
            float[] spectrumData = new float[barCount];
            for (int i = 0; i < barCount; i++) {
                spectrumData[i] = 0.2f; // 进一步提高默认能量值
            }
            return spectrumData;
        }
    }
}